package pCloud.server;

import java.net.InetSocketAddress;

import org.apache.log4j.Logger;

import pCloud.PCloudFactory;
import pCloud.config.PCloudConfig;
import pCloud.exception.InvalidConfiguration;
import pCloud.service.AccessControlHandle;
import pCloud.service.AuroraDataStreamHandle;
import pCloud.transport.Communicator;
import pCloud.transport.DatabaseAddress;

/**
 * Accept request from the client (after being forwarded by the proxy). Multiply
 * the value in the request and send it back as a response.
 * 
 */

/*
 *  Added by WWQ: Initialize DataStreamServer here too. For now, only stream server engine URI and schema directory
 * 
 */
public class DataServer extends ProxyServer {

	DatabaseHandle databaseHandler;

	DataStreamHandle streamHandler; // stream handle
	
	AccessControlHandle accessControlHandler;

	String driver, databaseId, username, password;
	
	String streamEngineAddress; // address of the streamEngine
	String streamSchemaDir; //directory of all stream schema files;
	int port;
	int streamEngineport; // port of the streamEngine
	
	public DataServer(int port, int serverId, String driver, String databaseId,
			String username, String password) {
		super(port, serverId);
		this.port = port;
		databaseHandler = null;
		this.accessControlHandler = null;
		this.driver = driver;
		this.databaseId = databaseId;
		this.username = username;
		this.password = password;
	}
	
	//wwq: Constructor with stream engine configurations.
	public DataServer(int port, int serverId, String driver, String databaseId,
			String username, String password,String streamEngineAddress,int streamEngineport,String streamSchemaDir) {
		super(port, serverId);
		this.port = port;
		databaseHandler = null;
		streamHandler = null;
		this.accessControlHandler = null;
		this.driver = driver;
		this.databaseId = databaseId;
		this.username = username;
		this.password = password;
		this.streamEngineport = streamEngineport;
		this.streamEngineAddress = streamEngineAddress;
		this.streamSchemaDir = streamSchemaDir;
	}

	
	public void init() throws Exception {
		logger = initLog("DataServer" + serverId,
				PCloudConfig.PCLOUD_DATA_SERVER_LOG);
		this.communicator.init(logger);

		this.communicator.addToDataIdMap(PCloudConfig
				.getString(PCloudConfig.PCLOUD_DATA_SERVER_ID),
				new DatabaseAddress(new InetSocketAddress("localhost", port),
						databaseId));

		
		this.initDatabaseHandler(this.driver, this.databaseId, this.username,this.password);

		//wwq: How to start stream data handler?
			
		this.initDataStreamHandler(this.streamSchemaDir,this.streamEngineAddress, this.streamEngineport);
		
		this.initAccessControl();

		this.acceptingClientConnections();
		
	}

	private void initDatabaseHandler(String driver, String databaseId,
			String username, String password) throws Exception {
		this.databaseHandler = PCloudFactory
				.getNewDatabaseHandleInstance(PCloudConfig
						.getString(PCloudConfig.PCLOUD_DATABASE_PROT));

		this.databaseHandler.init(driver, databaseId, username, password);
	}

	//wwq: initialize data stream handler
	
	private void initDataStreamHandler(String schemaDir,String address, int port) throws Exception {
		
		this.streamHandler = PCloudFactory.
				getNewDataStreamHandle(PCloudConfig.
						getString(PCloudConfig.PCLOUD_STREAM_ENGINE_PROT));
		this.streamHandler.init(schemaDir,address, port);
		
		System.out.println(this.streamHandler.toString());
	}
	
	private void initAccessControl() throws Exception {
		this.accessControlHandler = PCloudFactory
				.getNewAccessControlHandleInstance(PCloudConfig
						.getString(PCloudConfig.PCLOUD_ACCESS_CONTROL_PROT));
		this.accessControlHandler.init();
	}

	void acceptingClientConnections() throws Exception {
		new DataServerAcceptor(this.listeningPort, this.communicator,
				this.databaseHandler, this.accessControlHandler).accept();
	}

	public static void main(String[] args) throws Exception {

		// 2 data servers, run both instances
		String configFile = PCloudConfig.DEFAULT_CONFIG_FILE;

		if (args.length > 0) {
			configFile = args[0];
		}
		
		PCloudConfig.init(configFile);
		String[] addr = PCloudConfig.getString(
				PCloudConfig.PCLOUD_DATA_SERVER_ADDRESS).split(" ");

		int nServers = new Integer(addr[0]).intValue();
		if (nServers != 1)
			throw new InvalidConfiguration(
					"Number of (local) data server should be 1");

		int serverPort = new Integer(addr[2]).intValue();

		String driver = PCloudConfig.getDatabaseDriver();
		String username = PCloudConfig.getDatabaseUserName();
		String password = PCloudConfig.getDatabasePassword();
		String stream_engine_address = PCloudConfig.getStreamEngineAddress();
		int stream_engine_port = new Integer(PCloudConfig.getStreamEnginePort()).intValue();
		String stream_schema_dir = PCloudConfig.getStreamSchemaDir();

		String databaseId = PCloudConfig
				.getString(PCloudConfig.PCLOUD_DATA_SERVER_DATABASE);

		System.err.println("Initalizing data server at port " + serverPort
				+ ", with database " + databaseId);
		
		//DataServer ds = new DataServer(serverPort, 0, driver, databaseId,username, password);
		DataServer ds = new DataServer(serverPort, 0, driver, databaseId,username, password,stream_engine_address,stream_engine_port,stream_schema_dir);
		
		ds.init();
		
	}
}
